﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using IChessEngine;

namespace ChessEngine.Gaming
{
    public class ChsGame : ChsGameInfo, IChsGame
    {
        //esta implementación va a implementar el patron "Lazy Inicialization"; no se calcularà
        //una posición hasta que sea estrictamente necesario.
        private IChsPositionRepository positionRepository = null;
        private IChsMovementRepository movementRepository = null;
        private static IChsValidator validator = ChsFactory.CreateValidator();
        public ChsGame()
        {
            positionRepository = ChsFactory.CreatePositionRepository();
            movementRepository = ChsFactory.CreateMovementRepository();
            positionRepository.Add(ChsFactory.CreateGamePosition(Guid.Empty, ChsFactory.CreatePosition()));
        }
        public ChsGame(Guid _id,
                            string _whitePlayer,
                            string _blackPlayer,
                            DateTime _date) :
            base(_id, _whitePlayer, _blackPlayer, _date)
        {
            positionRepository = ChsFactory.CreatePositionRepository();
            movementRepository = ChsFactory.CreateMovementRepository();
            positionRepository.Add(ChsFactory.CreateGamePosition(Guid.Empty, ChsFactory.CreatePosition()));
        }
        public IChsGamePosition GetRootPosition()
        {
            return positionRepository.GetPosition(Guid.Empty);
        }
        public IChsGameMovement AddMovement(IChsGamePosition _currentPosition, IChsMovement movement)
        {
            List<IChsGameMovement> existingMovements = movementRepository.FindMovementsBySource(_currentPosition.Id);
            foreach (IChsGameMovement existingMovement in existingMovements)
                if ((existingMovement.Target.X == movement.Target.X) &&
                    (existingMovement.Source.X == movement.Source.X) &&
                    (existingMovement.Target.Y == movement.Target.Y) &&
                    (existingMovement.Source.Y == movement.Source.Y))
                    return existingMovement;
            IChsPosition nextPosition = null;
            if (!validator.ValidateMovement(_currentPosition, out nextPosition, movement))
                return null;
            IChsGamePosition nextGamePosition = ChsFactory.CreateGamePosition(nextPosition);
            positionRepository.Add(nextGamePosition);
            IChsGameMovement gameMovement =
                ChsFactory.CreateGameMovement(movement,
                                              _currentPosition.Id,
                                              nextGamePosition.Id);
            gameMovement.PgnNotation = CalculatePgnNotation(_currentPosition,
                                                            nextGamePosition,
                                                            gameMovement);
            movementRepository.Add(gameMovement);
            return gameMovement;
        }
        public void AddMovement(IChsGameMovement gameMovement)
        {
            movementRepository.Add(gameMovement);
        }
        private string CalculatePgnNotation(IChsGamePosition currentPosition, IChsGamePosition nextPosition, IChsGameMovement movement)
        {
            //TODO: calcular la notacion pgn del movimiento
            EChsPieceType type = currentPosition.GetPieceAt(movement.Source).Type;
            if (currentPosition.HasEnemyPiece(movement.Target))
            {
                switch (type)
                {
                    case EChsPieceType.PAWN:
                        return String.Format("{0}x{1}{2}", movement.Source.ColumnString, movement.Target, (nextPosition.AllyKingCheck)?"+":"");
                    case EChsPieceType.KNIGHT:
                        return String.Format("C{0}x{1}{2}", 
                            validator.IsMovementUniqueByType(currentPosition, movement)?"":movement.Source.ToString(),
                            movement.Target, (nextPosition.AllyKingCheck) ? "+" : "");
                    case EChsPieceType.BISHOP:
                        return String.Format("A{0}x{1}{2}",
                            validator.IsMovementUniqueByType(currentPosition, movement) ? "" : movement.Source.ToString(),
                            movement.Target, (nextPosition.AllyKingCheck) ? "+" : "");
                    case EChsPieceType.ROCK:
                        return String.Format("T{0}x{1}{2}",
                            validator.IsMovementUniqueByType(currentPosition, movement) ? "" : movement.Source.ToString(),
                            movement.Target, (nextPosition.AllyKingCheck) ? "+" : "");
                    case EChsPieceType.QUEEN:
                        return String.Format("D{0}x{1}{2}",
                            validator.IsMovementUniqueByType(currentPosition, movement) ? "" : movement.Source.ToString(),
                            movement.Target, (nextPosition.AllyKingCheck) ? "+" : "");
                    case EChsPieceType.KING:
                        return String.Format("R{0}x{1}{2}",
                            validator.IsMovementUniqueByType(currentPosition, movement) ? "" : movement.Source.ToString(),
                            movement.Target, (nextPosition.AllyKingCheck) ? "+" : "");
                }
            }
            else
            {
                switch (type)
                {
                    case EChsPieceType.PAWN:
                        return String.Format("{0}{1}", movement.Target, (nextPosition.AllyKingCheck) ? "+" : "");
                    case EChsPieceType.KNIGHT:
                        return String.Format("C{0}{1}{2}",
                            validator.IsMovementUniqueByType(currentPosition, movement) ? "" : movement.Source.ToString(),
                            movement.Target, (nextPosition.AllyKingCheck) ? "+" : "");
                    case EChsPieceType.BISHOP:
                        return String.Format("A{0}{1}{2}",
                            validator.IsMovementUniqueByType(currentPosition, movement) ? "" : movement.Source.ToString(),
                            movement.Target, (nextPosition.AllyKingCheck) ? "+" : "");
                    case EChsPieceType.ROCK:
                        return String.Format("T{0}{1}{2}",
                            validator.IsMovementUniqueByType(currentPosition, movement) ? "" : movement.Source.ToString(),
                            movement.Target, (nextPosition.AllyKingCheck) ? "+" : "");
                    case EChsPieceType.QUEEN:
                        return String.Format("D{0}{1}{2}",
                            validator.IsMovementUniqueByType(currentPosition, movement) ? "" : movement.Source.ToString(),
                            movement.Target, (nextPosition.AllyKingCheck) ? "+" : "");
                    case EChsPieceType.KING:
                        return String.Format("R{0}{1}{2}",
                            validator.IsMovementUniqueByType(currentPosition, movement) ? "" : movement.Source.ToString(),
                            movement.Target, (nextPosition.AllyKingCheck) ? "+" : "");
                }
            }
            return "";
        }
        public IChsGamePosition GetTargetPosition(IChsGameMovement movement)
        {
            return GetTargetPosition(movement.TargetPositionId);
        }
        public IChsGamePosition GetTargetPosition(Guid positionId)
        {
            //aquí es donde se lleva a cabo la "Lazy incialization" recuperando las
            //posiciones de forma recursiva...
            IChsGamePosition position = positionRepository.GetPosition(positionId);
            if (position == null)
            {
                IChsGameMovement movement =
                    movementRepository.FindMovementByTarget(positionId);
                position = GetTargetPosition(movement.SourcePositionId);
                positionRepository.Add(
                    ChsFactory.CreateGamePosition(positionId, position.Execute(movement)));
            }
            return positionRepository.GetPosition(positionId);
        }
        public IChsGamePosition FindBackwardPosition(IChsGamePosition currentPosition)
        {
            IChsGameMovement movement = movementRepository.FindMovementByTarget(currentPosition.Id);
            if (movement == null)
                return null;
            else
                return positionRepository.GetPosition(movement.SourcePositionId);
        }
        public List<IChsGameMovement> FindForwardMovements(IChsGamePosition currentPosition)
        {
            return movementRepository.FindMovementsBySource(currentPosition.Id);
        }
        public List<IChsGameMovement> Movements
        {
            get 
            {
                return movementRepository.Movements;
            }
        }
    }
}
